﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;

namespace SpaceGame
{
    //SpriteTable class
    //Use property CurrentSprite and methods addSprite(String,Sprite), and switchSprite(String)
    public class SpriteTable
    {
        private Dictionary<String, Sprite> sprites;
        private Sprite currentSprite;

        //Property to access current sprite
        public Sprite CurrentSprite
        {
            get { return currentSprite; }
        }

        public SpriteTable()
        {
            sprites = new Dictionary<String, Sprite>();
        }

        //Add a sprite, using a given keyname
        public bool addSprite(String key, Sprite sprite)
        {
            if (sprite == null)
                return false;
            try
            {
                sprites.Add(key, sprite);
                //Set the current sprite to the first available sprite
                if (sprites.Count == 1)
                {
                    currentSprite = sprite;
                }
                return true;
            }
            catch (Exception e)
            {
                if (e is ArgumentException || e is ArgumentNullException)
                {
                    return false;
                }
                throw;
            }
        }

        public bool delSprite(String key)
        {
            return sprites.Remove(key);
        }

        //Attempts to switch to a given sprite, returning true if succeeded, false otherwise
        public bool switchSprite(String key)
        {
            try
            {
                currentSprite = sprites[key];
                currentSprite.restartAnimation();
                return true;
            }
            catch (KeyNotFoundException e)
            {
                return false;
            }
        }

        public Sprite getSprite(String key)
        {
            try
            {
                return sprites[key];
            }
            catch (KeyNotFoundException e)
            {
                return null;
            }
        }
    }

    public class Sprite
    {
        private Stopwatch fpsTimer = Stopwatch.StartNew();  //keep track of elapsed ms since last frame change

        private Texture2D texture;
        private float fps;
        private bool looped = true;
        private int width;  //texture width and height, nonscaled
        private int height;
        private List<Point> frames; // x,y coords top-left corner of sprite frames
        private int frame = 0;    //the actual frame that we should be on
        Vector2 scale = new Vector2(1,1);

        // The texture is the full image
        public Texture2D Texture
        {
            get { return texture; }
        }

        public void restartAnimation()
        {
            fpsTimer.Restart();
            frame = 0;
        }


        public int Width
        {
            get { return width; }
        }
        public int Height
        {
            get { return height; }
        }

        //Scale sprite to a new texture size; returns scaling ratios
        public Vector2 scaleTo(Vector2 newSize)
        {
            scale = new Vector2(newSize.X / width, newSize.Y / height);
            return scale;
        }

        //Scale sprite using a ratio
        public void scaleToPercent(Vector2 scale)
        {
            this.scale = scale;
        }

        private Rectangle getFrameRect()
        {
            return new Rectangle(frames[frame].X, frames[frame].Y, width, height);
        }

        // If this sprite is animated, how many
        // frames per second
        public float FPS
        {
            get { return fps; }
            set { fps = value; }
        }

        // returns whether or not this sprite loops
        public bool Looped
        {
            get { return looped; }
            set { looped = value; }
        }

        // Number of frames in this sprite
        public int FrameCount
        {
            get { return frames.Count; }
        }

        public void addFrame(Point coords)
        {
            addFrame(coords.X, coords.Y);
        }

        // Add a frame to this sprite where x,y are top left corner
        public void addFrame(int x, int y)
        {
            if ((x <= texture.Width - width && x >= 0) && (y <= texture.Height - height && y >= 0))
            {
                frames.Add(new Point(x, y));
            }
            else
            {
                throw new InvalidCoordinateException();
            }
        }

        public void Draw(Vector2 position, float rotation, float z, SpriteBatch spriteBatch)
        {
            Draw(position, rotation, z, false, spriteBatch);
        }

        public void Draw(Vector2 position, float rotation, float z, bool flipped, SpriteBatch spriteBatch)
        {
            SpriteEffects horizontalFlip = flipped ? SpriteEffects.None : SpriteEffects.FlipHorizontally;
            float mspf = 1000f / fps;    // ms per frame
            if (fpsTimer.ElapsedMilliseconds > mspf)
            {
                frame++;
                fpsTimer.Restart();
            }
            //if we've reached the end of the animation, then either cycle back to 1st frame or stop at last frame
            if (frame >= FrameCount)
                frame = looped ? 0 : FrameCount - 1;
            Vector2 origin = new Vector2(texture.Width / 2, texture.Height / 2);    //MAY NEED TO BE CHANGED
            spriteBatch.Draw(Texture, position, getFrameRect(), Color.White, rotation, origin, scale, horizontalFlip, z);
        }


        // For sprites with one frame (use entire texture)
        public Sprite(Texture2D texture)
        {
            this.texture = texture;
            this.height = texture.Height;
            this.width = texture.Width;
            scale = new Vector2(1, 1);
            this.frames = new List<Point>();
            frames.Add(new Point(0, 0));
            frame = 0;
            this.fps = 0f;
            this.looped = false;
        }

        // For sprites with multiple frames, specify the frames per second
        public Sprite(Texture2D texture, Vector2 origin, int height, int width, float fps, bool looped) : this(texture)
        {
            this.height = height;
            this.width = width;
            this.fps = fps;
            this.looped = looped;
        }
    }
}


class InvalidCoordinateException : Exception
{

}